package com.bestv.search.management.command;

import com.bestv.search.common.cache.SystemCache;
import com.bestv.search.common.dataSource.DataSourceKey;
import com.bestv.search.common.model.*;
import com.bestv.search.common.service.InfoManager;
import com.bestv.search.common.service.ManualBindManager;
import com.bestv.search.common.util.DateUtil;
import com.bestv.search.management.xfire.ISearchWS;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.xfire.client.XFireProxyFactory;
import org.codehaus.xfire.service.Service;
import org.codehaus.xfire.service.binding.ObjectServiceFactory;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.util.StringUtils;

import java.net.MalformedURLException;
import java.util.*;

public class SearchCommand {

    protected final Log log = LogFactory.getLog(getClass());

    private InfoManager infoManager;
    private ManualBindManager manualBindManager;
    private SystemCache systemCache;

    public InfoManager getInfoManager() {
        return infoManager;
    }

    public void setInfoManager(InfoManager infoManager) {
        this.infoManager = infoManager;
    }

    /**
     * Search result for manual bind Fake search,means just fuzzy select from
     * Database
     *
     * @param user system user
     * @param map  param map
     * @return search results in json format
     * @throws JSONException object to json exception
     */
    public String searchInfoByManualId(SysUser user, Map<String, String[]> map, DataSourceKey dsKey)
            throws JSONException {
        String result = "";
        int start;
        int limit;
        String[] sstart = map.get("start");
        String[] slimit = map.get("limit");
        start = Integer.parseInt(sstart[0]);
        limit = Integer.parseInt(slimit[0]);
        String[] mid = map.get("manualId");
        String[] access = map.get("acs_type");
        String accessFlag = "";
        if (access != null && !"".equals(access[0]))
            accessFlag = access[0].trim();

        log.debug("manualId: " + mid[0]);
        ManualBind mb = manualBindManager.getManualBindById(dsKey, new Long(mid[0]));
        if (mb != null) {
            JSONArray array = new JSONArray();
            int totalCount = mb.getInfos().size();
            ArrayList<Info> list = new ArrayList<Info>(mb.getInfos());
            if (start + limit > totalCount) {
                for (int i = start; i < totalCount; i++)
                    array.put(infoToJSONObject(list.get(i), accessFlag));
            } else {
                for (int i = start; i < start + limit; i++)
                    array.put(infoToJSONObject(list.get(i), accessFlag));
            }
            result = "{'totalCount':'" + totalCount + "','infos':" + array.toString() + "}";
        }
        return result;
    }

    public ManualBindManager getManualBindManager() {
        return manualBindManager;
    }

    public void setManualBindManager(ManualBindManager manualBindManager) {
        this.manualBindManager = manualBindManager;
    }

    /**
     * Call Web Service Function from Search Engine Search for preview page
     * searchPreview.htm. Also search for keysubject.htm page when you search
     * keywords from infos for KeySubject.keywords
     *
     * @param user system user
     * @param map  params map
     * @return search results in json format
     * @throws JSONException object to json exception
     */
    public String searchPreview(SysUser user, Map<String, String[]> map, DataSourceKey dsKey) throws JSONException {
        String[] stat = map.get("start");
        String[] slimit = map.get("limit");
        String[] json = map.get("json");
        String[] access = map.get("acs_type");
        String accessFlag = "";
        if (access != null && !"".equals(access[0]))
            accessFlag = access[0].trim();

        JSONObject jsonObj = new JSONObject(json[0]);
        if (!"".equals(stat[0]))
            jsonObj.put("start", Integer.parseInt(stat[0]));
        if (!"".equals(slimit[0]))
            jsonObj.put("limit", Integer.parseInt(slimit[0]));
        Service serviceModel = new ObjectServiceFactory().create(ISearchWS.class);
        String url = systemCache.getWebServiceEngineUrls().get(dsKey.toString());
        String[] soapUrls = StringUtils.tokenizeToStringArray(url, SystemCache.WEB_SERVICE_EURL_DELIMITERS);

        if (soapUrls == null || soapUrls.length == 0) {
            log.error("Gets info failed, no web service interface found from site " + dsKey.toString());
            return "{'totalCount':0,'results':[error]}";
        }

        ISearchWS service;
        String remoteRt = "";
        try {
            service = (ISearchWS) new XFireProxyFactory().create(serviceModel, soapUrls[0]);
            remoteRt = service.searchPreview(jsonObj.toString());
        } catch (MalformedURLException e) {
            log.error(e);
        } catch (NullPointerException e) {
            log.error(e);
        }
        log.debug("engine return: " + remoteRt);
        if (remoteRt.matches("^\\([\\d, ]+\\)&\\d+")) {
            log.debug("remoteRt match the RegExp");
            String[] semiRts = remoteRt.split("&");
            if (semiRts.length != 2)
                return "Result from Enigne error!";

            Set<Info> infos = infoManager.getInfoSetByIds(dsKey, semiRts[0]);
            if (infos == null)
                return "{\"totalCount\":0,\"results\":[]}";
            // 调整顺序
            Map<Long, Info> infoMap = new HashMap<Long, Info>();
            for (Info info : infos)
                infoMap.put(info.getId(), info);
            infos.clear();
            JSONArray array = new JSONArray();
            String[] ids = semiRts[0].substring(1, semiRts[0].length() - 1).split(",");
            for (String id : ids) {
                Info ifo = infoMap.get(new Long(id));
                if (ifo == null)
                    continue;
                JSONObject jo = infoToJSONObject(infoMap.get(new Long(id)), accessFlag);
                array.put(jo);
            }
            log.debug("result: " + array.toString());
            return "{\"totalCount\":\"" + Long.parseLong(semiRts[1]) + "\",\"results\":" + array.toString() + "}";
        }
        if ("noDoc".equals(remoteRt))
            return "{\"totalCount\":0,\"results\":[]}";
        return remoteRt;
    }

    /**
     * Call Web Service Function from Search Engine Search in keyword or tag
     * field,and in pointed p_type by one key word,if the p_type == 'all'， then
     * all ptype will be searched The page keyStat.htm when open a
     * panel(关键字.p_type) will presents all keywords,when you click any one,this
     * function will be called and in this case the field='keyword' The page
     * keyStat.htm when open a panel(p_type.标签) will presents all keywords,when
     * you click any one,this function will be called and in this case the
     * field='tag'
     *
     * @param user system user
     * @param map  param map
     * @return the (ids) of Info from Search Engine
     * @throws JSONException json exception
     */
    public String searchInOneFieldAndPtypeByOneKeyword(SysUser user, Map<String, String[]> map, DataSourceKey dsKey)
            throws JSONException {
        String[] key = map.get("key");
        String[] field = map.get("field");
        String[] ptype = map.get("ptype");
        String[] start = map.get("start");
        String[] limit = map.get("limit");

        String url = systemCache.getWebServiceEngineUrls().get(dsKey.toString());
        String[] soapUrls = StringUtils.tokenizeToStringArray(url, SystemCache.WEB_SERVICE_EURL_DELIMITERS);
        if (soapUrls == null || soapUrls.length == 0) {
            log.error("Gets info failed, no web service interface found from site " + dsKey.toString());
            return "{'totalCount':0,'results':[error]}";
        }

        Service serviceModel = new ObjectServiceFactory().create(ISearchWS.class);
        ISearchWS service;
        String semiRt = "";
        try {
            service = (ISearchWS) new XFireProxyFactory().create(serviceModel, soapUrls[0]);
            semiRt = service.searchInOneFieldAndPtypeByOneKeyword(ptype[0], field[0], key[0],
                    Integer.parseInt(start[0]), Integer.parseInt(limit[0]));
        } catch (MalformedURLException e) {
            log.error(e);
        } catch (NullPointerException e) {
            log.error(e);
        }
        log.debug("Get From Engine: " + semiRt);
        if ("".equals(semiRt))
            return "{'totalCount':0,'results':[]}";
        String[] sr = semiRt.split("&");

        Set<Info> infos = infoManager.getInfoSetByIds(dsKey, sr[0]);
        if (infos == null || infos.size() == 0)
            return "{\"totalCount\":0,\"results\":[]}";
        Map<Long, Info> infoMap = new HashMap<Long, Info>();
        for (Info info : infos)
            infoMap.put(info.getId(), info);
        JSONArray array = new JSONArray();
        String[] ids = sr[0].substring(1, sr[0].length() - 1).split(",");
        for (String id : ids) {
            JSONObject jo = infoToJSONObject(infoMap.get(new Long(id)), "");
            array.put(jo);
        }
        return "{\"totalCount\":\"" + Long.parseLong(sr[1]) + "\",\"results\":" + array.toString() + "}";
    }

    private JSONObject infoToJSONObject(Info info, String accessType) throws JSONException {
        JSONObject jo = new JSONObject();
        jo.put("id", String.valueOf(info.getId()));
        jo.put("name", info.getName());
        jo.put("p_type", info.getP_type());
        jo.put("code", info.getCode());
        jo.put("updateTime",
                info.getUpdateTime() == null ? "" : DateUtil.convertDateToString("yyyy-MM-dd HH:mm",
                        info.getUpdateTime()));
        Set<Keyword> keySet = info.getKeywords();
        if (keySet == null || keySet.size() == 0)
            jo.put("keywords", "");
        else {
            StringBuffer sb = new StringBuffer();
            int i = 0;
            for (Keyword key : keySet) {
                sb.append(key.getKeyword());
                if (++i < keySet.size())
                    sb.append(" ");
            }
            jo.put("keywords", sb.toString());
        }
        if (info instanceof Entrance)
            jo.put("type", info.getType() == null ? "" : info.getType().getName());
        else if (info instanceof Program) {
            Program p = (Program) info;
            jo.put("type", info.getType() == null ? "" : info.getType().getName());
            if ("searchPreview".equals(accessType) || "".equals(accessType)) {
                jo.put("actors", p.getActors() == null ? "" : p.getActors());
                jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
            }
        } else if (info instanceof Series) {
            Series p = (Series) info;
            jo.put("type", info.getType() == null ? "" : info.getType().getName());
            if ("searchPreview".equals(accessType) || "".equals(accessType)) {
                jo.put("actors", p.getActors() == null ? "" : p.getActors());
                jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
            }
        } else if (info instanceof Schedule) {
            Schedule p = (Schedule) info;
            if ("searchPreview".equals(accessType) || "".equals(accessType)) {
                jo.put("actors", p.getActors() == null ? "" : p.getActors());
                jo.put("directors", p.getDirectors() == null ? "" : p.getDirectors());
            }
            if (p.getEndPlayTime().before(new Date()))
                jo.put("type", "回看");
            else
                jo.put("type", "预告");
        } else
            jo.put("type", info.getType() == null ? "" : info.getType().getName());
        if ("keysubject".equals(accessType))
            return jo;
        Set<Tag> tagSet = info.getTags();
        if (tagSet == null || tagSet.size() == 0)
            jo.put("tags", "");
        else {
            StringBuffer tagBf = new StringBuffer();
            int i = 0;
            for (Tag t : tagSet) {
                tagBf.append(t.getTag());
                if (++i < tagSet.size())
                    tagBf.append(",");
            }
            jo.put("tags", tagBf.toString());
        }
        jo.put("description", info.getDescription());
        return jo;
    }

    public SystemCache getSystemCache() {
        return systemCache;
    }

    public void setSystemCache(SystemCache systemCache) {
        this.systemCache = systemCache;
    }

}
